package com.ruoyi.erp.service.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.erp.domain.*;
import com.ruoyi.erp.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.erp.mapper.ProjectHourMapper;
import com.ruoyi.common.core.text.Convert;

/**
 * 项目工时Service业务层处理
 * 
 * @author sauger
 * @date 2022-04-06
 */
@Service
public class ProjectHourServiceImpl implements IProjectHourService 
{
    @Autowired
    private ProjectHourMapper projectHourMapper;
    @Autowired
    private IHolidayConfigService holidayConfigService;
    @Autowired
    private IProjectHourUserService projectHourUserService;
    @Autowired
    private IDingtalkProcessService processService;
    @Autowired
    private IProjectHourAbnormalService abnormalService;

    /**
     * 查询项目工时
     * 
     * @param id 项目工时主键
     * @return 项目工时
     */
    @Override
    public ProjectHour selectProjectHourById(Integer id)
    {
        return projectHourMapper.selectProjectHourById(id);
    }

    /**
     * 查询项目工时列表
     * 
     * @param projectHour 项目工时
     * @return 项目工时
     */
    @Override
    public List<ProjectHour> selectProjectHourList(ProjectHour projectHour)
    {
        return projectHourMapper.selectProjectHourList(projectHour);
    }

    /**
     * 新增项目工时
     * 
     * @param projectHour 项目工时
     * @return 结果
     */
    @Override
    public int insertProjectHour(ProjectHour projectHour)
    {
        return projectHourMapper.insertProjectHour(projectHour);
    }

    /**
     * 修改项目工时
     * 
     * @param projectHour 项目工时
     * @return 结果
     */
    @Override
    public int updateProjectHour(ProjectHour projectHour)
    {
        return projectHourMapper.updateProjectHour(projectHour);
    }

    /**
     * 批量删除项目工时
     * 
     * @param ids 需要删除的项目工时主键
     * @return 结果
     */
    @Override
    public int deleteProjectHourByIds(String ids)
    {
        return projectHourMapper.deleteProjectHourByIds(Convert.toStrArray(ids));
    }

    /**
     * 删除项目工时信息
     * 
     * @param id 项目工时主键
     * @return 结果
     */
    @Override
    public int deleteProjectHourById(Integer id)
    {
        return projectHourMapper.deleteProjectHourById(id);
    }

    /**
     * 根据日期汇总工时
     *
     * @param projectHour
     * @return
     */
    @Override
    public List<ProjectHour> selectProjectHourGroupByDate(ProjectHour projectHour) {
        return projectHourMapper.selectProjectHourGroupByDate(projectHour);
    }

    /**
     * 获取指定时间的用户工时，包含应该填，但未填得
     *
     * @param holidayConfigTypeId
     * @param beginDate
     * @param endDate
     * @return
     */
    @Override
    public List<DateWorkHour> getAllDateWorkHours(Integer holidayConfigTypeId, Date beginDate, Date endDate) {
        List<DateWorkHour> dateWorkHours = new ArrayList<>();
        //有调休的例外日期
        List<HolidayConfig> holidayConfigs = holidayConfigService.selectHolidayConfigInDuration(beginDate,endDate, holidayConfigTypeId);
        //获取在指定时间段内，需要登记工时的人员列表
        List<ProjectHourUser> users = projectHourUserService.selectProjectHourUsersByDuration(beginDate, endDate);
        //获取时间段内请假和加班的数据
        DingtalkProcess process = new DingtalkProcess();
        process.getParams().put("beginDate", beginDate);
        process.getParams().put("endDate", endDate);
        List<DingtalkProcess> processes = processService.selectDingtalkProcessList(process);

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        users.stream().forEach(user->{
            ProjectHour search = new ProjectHour();
            search.setUserId(user.getUserId());
            search.getParams().put("beginTime", format.format(beginDate));
            search.getParams().put("endTime", format.format(endDate));
            List<ProjectHour> projectHours = selectProjectHourGroupByDate(search);
            Date startDate = beginDate.compareTo(user.getBeginDate()) < 0 ? new Date(user.getBeginDate().getTime()) : new Date(beginDate.getTime());
            Date finishDate = user.getEndDate() == null || user.getEndDate().compareTo(endDate) > 0 ? new Date(endDate.getTime()) : new Date(user.getEndDate().getTime());
            Date compareDate = startDate;
            while (compareDate.compareTo(finishDate) <= 0)
            {
                DateWorkHour dateWorkHour = this.getUserWorkHour(user,compareDate,holidayConfigs,processes,projectHours);
                dateWorkHours.add(dateWorkHour);
                compareDate = DateUtils.addDays(compareDate, 1);
            }
        });
        return dateWorkHours;
    }

    private DateWorkHour getUserWorkHour(ProjectHourUser user, Date date, List<HolidayConfig> holidayConfig, List<DingtalkProcess> dingtalkProcess, List<ProjectHour> projectHours)
    {
        DateWorkHour dateWorkHour = new DateWorkHour();
        dateWorkHour.setWorkDate(date);
        dateWorkHour.setUserId(user.getUserId());
        dateWorkHour.setWorkHour(BigDecimal.ZERO);
        if (projectHours != null)
        {
            Optional<ProjectHour> projectHour = projectHours.stream().filter(f->f.getWorkDate().equals(date)).findFirst();
            if (projectHour.isPresent()){
                dateWorkHour.setWorkHour(projectHour.get().getWorkHour());
            }
        }

        if(this.holidayConfigService.isWorkday(date, holidayConfig)){
            //检查是否有请假
            Optional<DingtalkProcess> process = dingtalkProcess.stream().filter(p -> p.getUserId().equals(user.getUserId()) && p.getProcessDate().equals(date) && p.getProcessType().equals("leave")).findFirst();
            if (process.isPresent()){
                dateWorkHour.setDateType("leave");
                dateWorkHour.setNeedHour(BigDecimal.valueOf(8).subtract(process.get().getProcessHour()));
                if (dateWorkHour.getWorkHour().compareTo(BigDecimal.ZERO)<0){
                    dateWorkHour.setWorkHour(BigDecimal.ZERO);
                }
            }else {
                dateWorkHour.setDateType("workday");
                dateWorkHour.setNeedHour(BigDecimal.valueOf(8));
            }
        }else
        {
            //检查是否有加班
            Optional<DingtalkProcess> process = dingtalkProcess.stream().filter(p -> p.getUserId().equals(user.getUserId()) && p.getProcessDate().equals(date) && p.getProcessType().equals("overtime")).findFirst();
            if (process.isPresent()){
                dateWorkHour.setDateType("overtime");
                dateWorkHour.setNeedHour(process.get().getProcessHour());
            }else {
                dateWorkHour.setDateType("holiday");
                dateWorkHour.setNeedHour(BigDecimal.ZERO);
            }
        }
        return dateWorkHour;
    }

    /**
     * 获取指定时间范围内的工时汇总信息
     *
     * @param holidayConfigTypeId
     * @param beginDate
     * @param endDate
     * @return
     */
    @Override
    public ProjectHourReport getProjectHourReport(Integer holidayConfigTypeId, Date beginDate, Date endDate) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String strBeginTime = format.format(beginDate);
        String strEndTime = format.format(endDate);
        List<DateWorkHour> dateWorkHourList = this.getAllDateWorkHours(holidayConfigTypeId, beginDate, endDate);
        ProjectHourReport report = new ProjectHourReport();
        report.setBeginDate(beginDate);
        report.setEndDate(endDate);
        report.setHolidayConfigId(holidayConfigTypeId);
        BigDecimal needHours = dateWorkHourList.stream().map(DateWorkHour::getNeedHour).reduce(BigDecimal.ZERO, BigDecimal::add);
        report.setNeedHour(needHours);
        BigDecimal workHours = dateWorkHourList.stream().map(DateWorkHour::getWorkHour).reduce(BigDecimal.ZERO, BigDecimal::add);
        ProjectHour projectHour = new ProjectHour();

        projectHour.getParams().put("beginTime", strBeginTime);
        projectHour.getParams().put("endTime", strEndTime);
        List<ProjectHour> projectHourList = projectHourMapper.selectProjectHourGroupByType(projectHour);
        Optional<ProjectHour> normalProjectHour = projectHourList.stream().filter(f->f.getType()==1).findFirst();
        BigDecimal normalHours = normalProjectHour.isPresent() ? normalProjectHour.get().getWorkHour() : BigDecimal.ZERO;
        Optional<ProjectHour> maintainProjectHour = projectHourList.stream().filter(f->f.getType() ==2).findFirst();
        BigDecimal maintainHour = maintainProjectHour.isPresent() ? maintainProjectHour.get().getWorkHour() : BigDecimal.ZERO;
        Optional<ProjectHour> freeProjectHour = projectHourList.stream().filter(f->f.getType() == 3).findFirst();
        BigDecimal freeHours = freeProjectHour.isPresent() ? freeProjectHour.get().getWorkHour() : BigDecimal.ZERO;
        report.setNormalWorkHours(normalHours);
        report.setFreeWorkHours(freeHours);
        report.setMaintainWorkHours(maintainHour);

        //处理加班和请假数据
        DingtalkProcess process = new DingtalkProcess();
        process.getParams().put("beginDate", beginDate);
        process.getParams().put("endDate", endDate);
        List<DingtalkProcess> processes = processService.selectDingtalkProcessList(process);
        report.setLeaveHours(processes.stream().filter(f->f.getProcessType().equals("leave")).map(DingtalkProcess::getProcessHour).reduce(BigDecimal.ZERO, BigDecimal::add));
        report.setLeaveCount((int)processes.stream().filter(f->f.getProcessType().equals("leave")).count());
        report.setOvertimeHours(processes.stream().filter(f->f.getProcessType().equals("overtime")).map(DingtalkProcess::getProcessHour).reduce(BigDecimal.ZERO, BigDecimal::add));
        report.setOvertimeCount((int)processes.stream().filter(f->f.getProcessType().equals("overtime")).count());

        //处理工时异常
        ProjectHourAbnormal search = new ProjectHourAbnormal();
        search.getParams().put("beginTime", strBeginTime);
        search.getParams().put("endTime", strEndTime);
        ProjectHourAbnormal abnormal = abnormalService.groupProjectHourAbnormal(search);
        report.setAbnormalCount(abnormal.getId());
        report.setAbnormalHours(abnormal.getLessHour());
        return report;
    }
}
